
// This file is part of Module Proxy.

// Module Proxy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Module Proxy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Module Proxy.  If not, see <https://www.gnu.org/licenses/>.


//         Copyright (C) 2021 - 2030  关中麦客  
//         All rights reserved
//
//         conf.rs
//         加载并解析配置文件
//
//         created by 关中麦客 1036038462@qq.com

use serde_derive;
use toml;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use once_cell::sync::OnceCell;
use serde_derive::Deserialize;

static CONFIG: OnceCell<Config> = OnceCell::new();    //全局缓存

#[derive(Deserialize)]
#[derive(Debug)]
struct Config 
{
    server: Server,
    log: Log,
    module: Module,
}

#[derive(Deserialize)]
#[derive(Debug)]
struct Server
{
    port: u16,
    root_path: String,
    chunked_size: u64,
    index: String,
} 

#[derive(Deserialize)]
#[derive(Debug)]
struct Log
{
    level: String,
} 

#[derive(Deserialize)]
#[derive(Debug)]
struct Module
{
    module_html: Vec<ModuleHtml>,
    module_http: Vec<ModuleHttp>,
    module_socket: Vec<ModuleSocket>,
} 

#[derive(Deserialize)]
#[derive(Debug)]
pub struct ModuleHtml
{
    pub module: String,
    pub path: String,
}

#[derive(Deserialize)]
#[derive(Debug)]
pub struct ModuleHttp
{
    pub module: String,
    pub proxy_pass: String,
}

#[derive(Deserialize)]
#[derive(Debug)]
pub struct ModuleSocket
{
    pub module: String,
    pub forward_pass: String,
}

/// 加载配置文件
pub fn load() -> Result<(), String>
{
    let option = conf_path();
    if None == option
    {
        return Err("Can't load config file".to_string());
    }  
    let pathbuf = option.unwrap();
    let file_path = pathbuf.as_path();      //配置文件路径

    match File::open(file_path) {
        Ok(file) => 
        {
            match parse_file(file)       //解析文件
            {
                Ok(config) =>
                {
                    CONFIG.get_or_init(||{config});         //全局变量
                    return Ok(());
                }
                Err(err_string) =>
                {
                    return Err(err_string);
                }
            }
        },
        Err(e) => 
        {
            println!{"parse config file error: {}", e};
            return Err(e.to_string());
        }
    };   
}

/// 获得网站根路径
pub fn root_path() -> &'static str
{
    if let Some(config) = CONFIG.get()
    {
        let root_path = &config.server.root_path;
        &root_path[..]
    }
    else
    {
        "/usr/local/modproxy/html"  //默认根路径
    }    
}

/// 获得日志级别
pub fn log_level() -> &'static str
{
    if let Some(config) = CONFIG.get()
    {
        let level = &config.log.level;
        &level[..]
    }
    else
    {
        "info"  //默认info级别
    }
}

/// 获得侦听端口
pub fn server_port() -> u16
{
    if let Some(config) = CONFIG.get()
    {
        config.server.port
    }
    else
    {
        80  //默认80端口
    }    
}

/// 获得chunked方式最小字节
pub fn chunked_size() -> u64
{
    if let Some(config) = CONFIG.get()
    {
        config.server.chunked_size
    }
    else
    {
        1024 * 1024 * 10  //默认10M
    } 
}

/// 获得index
pub fn index() -> Vec<&'static str>
{
    if let Some(config) = CONFIG.get()
    {
        let index_str = &config.server.index;
        let split = index_str.split_whitespace();
        let vec: Vec<&'static str> = split.collect();
        return vec;
    }

    Vec::new()
}

/// 所有socket module
pub fn all_module_socket() -> Vec<ModuleSocket>
{
    let mut vec: Vec<ModuleSocket> = vec!{};

    if let Some(config) = CONFIG.get()
    {
        for modsock in &config.module.module_socket
        {
            let one = ModuleSocket{
                module: modsock.module.clone(),
                forward_pass: modsock.forward_pass.clone(),
            };
            vec.push(one);
        }
    }

    vec
}

/// 所有http module
pub fn all_module_http() -> Vec<ModuleHttp>
{
    let mut vec: Vec<ModuleHttp> = vec!{};

    if let Some(config) = CONFIG.get()
    {
        for modhttp in &config.module.module_http
        {
            let one = ModuleHttp{
                module: modhttp.module.clone(),
                proxy_pass: modhttp.proxy_pass.clone(),
            };
            vec.push(one);
        }
    }

    vec
}

/// 匹配html module
pub fn module_html(module: &str) -> Option<ModuleHtml>
{
    if let Some(config) = CONFIG.get()
    {
        for modhtml in &config.module.module_html
        {
            if module == modhtml.module
            {
                let html = ModuleHtml{
                    module: modhtml.module.clone(),
                    path: modhtml.path.clone(),
                };

                return Some(html);
            }
        }
    }

    None
}


// --------------------------------------------------------------------------------------
//   私有函数
// --------------------------------------------------------------------------------------

//获取配置文件 default.toml 绝对路径
fn conf_path() -> Option<PathBuf>
{
    match std::env::current_exe() 
    {
        Ok(exe_path) => 
        {
            if let Some(dir) = exe_path.parent()
            {
                if let Some(dir) = dir.parent()
                {
                    let mut dir = dir.join("conf");
                    dir.push("default.toml");
                    return Some(dir);
                }
                else
                {
                    return None;
                }
            }
            else
            {
                return None;
            }
        },
        Err(e) => 
        {
            println!{"get config file path error: {}", e};
            return None;
        }
    };
}

/// 解析配置文件
///   成功返回Ok(()), 失败返回Err(e)
fn parse_file(mut file: File) -> Result<Config, String>
{
    //读配置文件
    let mut file_buf = String::new();
    if let Err(e) = file.read_to_string(&mut file_buf) 
    {
        println!{"read config file error: {}", e};
        return Err(e.to_string());
    };

    //解析配置
    match toml::from_str(&file_buf)
    {
        Ok(config) => 
        {
            return Ok(config);  //成功返回
        },
        Err(e) =>
        {
            println!{"parse config file error: {}", e};
            return Err(e.to_string());
        }
    }
}